﻿///Code based on
/// http://snipd.net/minimax-algorithm-with-alpha-beta-pruning-in-c

using System;
using System.Collections.Generic;
using System.Linq;
using Core.Models;

namespace Service.AI
{
    public class LearningAlphaBeta
    {
        private GameState Initial_Gs;
        private LearningNode rootNode;
        private LearningNode bestNode;
        private string BestMove;
        private List<double> StartWeight;
        private List<double> BestMoveScore;

        public LearningAlphaBeta(GameState p_gs, List<double> startWeight, string bestMove)
        {
            Initial_Gs = p_gs;
            StartWeight = startWeight;
            BestMove = bestMove;
        }

        public Tuple<MovesInfo, List<double>, List<double>> StartIteration(int depth, bool player)
        {
            rootNode = new LearningNode(Initial_Gs);
            var nodeScore = Iterate(rootNode, depth, int.MinValue, int.MaxValue, player);

            return new Tuple<MovesInfo, List<double>, List<double>>(bestNode.ogMove, BestMoveScore, bestNode.bestHeuristic);
        }

        public double Iterate(LearningNode node, int depth, double alpha, double beta, bool player)
        {
            if (depth == 0)
            {
                var boardScore = node.gs.GetBoardScore(Initial_Gs.WhoseMove, StartWeight);
                node.bestHeuristic = boardScore.Item2;
                node.Score = boardScore.Item1;
                return boardScore.Item1;
            }

            var childs = node.Children();

            if (!childs.Any())
            {
                var boardScore = node.gs.GetBoardScore(Initial_Gs.WhoseMove, StartWeight);
                node.bestHeuristic = boardScore.Item2;
                node.Score = boardScore.Item1;
                return boardScore.Item1;
            }

            if (player)
            {
                foreach (var child in childs)
                {
                    var nodeValue = Iterate(child, depth - 1, alpha, beta, false);

                    if (node.Score < nodeValue || node.Score == null)
                    {
                        node.Score = child.Score;
                        node.bestHeuristic = child.bestHeuristic;
                    }

                    //if its the GM move
                    if (node == rootNode && child.ogMove.WriteFullMove() == BestMove)
                    {
                        BestMoveScore = child.bestHeuristic;
                    }

                    if (nodeValue > alpha)
                    {
                        alpha = nodeValue;
                        if (node == rootNode || bestNode == null)
                        {
                            bestNode = child;
                        }
                    }

                    if (beta < alpha)
                    {
                        break;
                    }
                }

                return alpha;
            }
            else
            {
                foreach (var child in childs)
                {
                    var nodeValue = Iterate(child, depth - 1, alpha, beta, true);

                    if (node.Score < nodeValue || node.Score == null)
                    {
                        node.Score = nodeValue;
                        node.bestHeuristic = child.bestHeuristic;
                    }

                    beta = Math.Min(beta, nodeValue);
                    
                    if (beta < alpha)
                    {
                        break;
                    }
                }

                return beta;
            }
        }
    }
}